import type {
  FeedbackTagType,
  GreasyForkIdItem,
  GreasyForkStats,
  GreasyForkStatsItem,
  GreasyForkStorageCategory,
  GreasyForkStorageKey,
  GreasyForkUserType,
  ResolutionStatusType,
  ScriptFeedbackActionType,
  ScriptStatusActionType,
} from './types'
import { GMStorage } from '../../../../../../../../../lib/abstract'
import { ToolKit } from '../../../../../../../../../lib/utils/toolkit'
import { PROJECT_PREFIX, StorageType } from '../../../../../../../../../types/constants'
import {
  createEmptyStatsItem,
  createIdItem,
  DEFAULT_GREASY_FORK_STATS,
  UpdateOperation,
} from './types'

// 路径类型工具（忽略这里的递归深度报错！！！）
type PathImpl<T, Key extends keyof T> = Key extends string
  ? T[Key] extends Record<string, unknown>
    // @ts-expect-error: 忽略递归类型检查
    ? `${Key}.${Path<T[Key]>}` | Key
    : Key
  : never

type Path<T> = PathImpl<T, keyof T> | keyof T

type PathValue<T, P extends Path<T>> = P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
    ? Rest extends Path<T[Key]>
      ? PathValue<T[Key], Rest>
      : never
    : never
  : P extends keyof T
    ? T[P]
    : never

export class StorageProcessor extends GMStorage {
  id: string = 'greasy-fork-storage'
  name: string = 'StorageProcessor'
  private readonly STORAGE_KEY = `${PROJECT_PREFIX}-greasy-fork-stats`
  private static instance: StorageProcessor
  private storageType: StorageType = StorageType.GMStorage

  public static getInstance(): StorageProcessor {
    if (!StorageProcessor.instance) {
      StorageProcessor.instance = new StorageProcessor()
    }

    return StorageProcessor.instance
  }

  /**
   * 获取所有存储的统计数据
   */
  getAll(): GreasyForkStats {
    try {
      const storedData = this.storageType === StorageType.GMStorage
        ? this.storage.get(this.STORAGE_KEY)
        : JSON.parse(localStorage.getItem(this.STORAGE_KEY) || '{}')
      return storedData
        ? { ...DEFAULT_GREASY_FORK_STATS, ...storedData }
        : DEFAULT_GREASY_FORK_STATS
    }
    catch (error) {
      console.error('Failed to parse stored preferences:', error)
      return DEFAULT_GREASY_FORK_STATS
    }
  }

  /**
   * 获取特定路径的值
   * @param path 点分隔的路径字符串
   * @returns 路径对应的值，如果不存在则返回默认值
   */
  public get<P extends Path<GreasyForkStats>>(path: P): PathValue<GreasyForkStats, P> {
    const keys = path.split('.') as Array<keyof GreasyForkStats>
    let current: any = this.getAll()

    for (const key of keys) {
      if (current == null || typeof current !== 'object') {
        return this.getDefaultValue(path)
      }
      current = current[key]
    }

    return current !== undefined ? current : this.getDefaultValue(path)
  }

  /**
   * 设置特定路径的值
   * @param path 点分隔的路径字符串
   * @param value 要设置的值
   */
  public set<P extends Path<GreasyForkStats>>(
    path: P,
    value: PathValue<GreasyForkStats, P>,
  ): void {
    try {
      const keys = path.split('.') as Array<keyof GreasyForkStats>
      const config = this.getAll()
      let current: any = config

      for (let i = 0; i < keys.length - 1; i++) {
        const key = keys[i]
        if (current[key] == null || typeof current[key] !== 'object') {
          current[key] = {}
        }
        current = current[key]
      }

      current[keys[keys.length - 1]] = value
      if (this.storageType === StorageType.GMStorage) {
        this.storage.set(this.STORAGE_KEY, config)
      }
      else {
        localStorage.setItem(this.STORAGE_KEY, JSON.stringify(config))
      }
    }
    catch (error) {
      console.error('Failed to save preferences:', error)
    }
  }

  /**
   * 获取路径的默认值
   */
  private getDefaultValue<P extends Path<GreasyForkStats>>(path: P): PathValue<GreasyForkStats, P> {
    const keys = path.split('.') as Array<keyof GreasyForkStats>
    let current: any = DEFAULT_GREASY_FORK_STATS

    for (const key of keys) {
      if (current == null || typeof current !== 'object') {
        throw new Error(`Invalid path: ${path}`)
      }
      current = current[key]
    }

    return current
  }

  /**
   * 统一更新统计项
   * @param category 存储分类
   * @param key 统计项键名
   * @param id 要添加/删除的 ID
   * @param operation 操作类型
   * @param remark 备注
   */
  public updateStorageItem<C extends GreasyForkStorageCategory>(
    category: C,
    key: GreasyForkStorageKey<C>,
    id: string,
    operation: UpdateOperation,
    remark: string = '',
  ): void {
    const currentData = this.get(category) as Record<string, GreasyForkStatsItem>
    const updatedData = { ...currentData }

    // 获取或初始化当前项
    const currentItem = updatedData[key as string] || createEmptyStatsItem()

    // 更新当前项
    let updatedIds: GreasyForkIdItem[]

    if (operation === UpdateOperation.Add) {
      // 处理互斥关系
      this.handleMutualExclusion(key, id, updatedData)

      // 添加或更新ID项
      const existingIndex = currentItem.ids.findIndex(item => String(item.id) === String(id))
      if (existingIndex >= 0) {
        updatedIds = [...currentItem.ids]
        updatedIds[existingIndex] = createIdItem(id)
      }
      else {
        updatedIds = [...currentItem.ids, createIdItem(id)]
      }
    }
    else {
      // 移除ID项
      updatedIds = currentItem.ids.filter(item => String(item.id) !== String(id))
    }

    // 更新数据
    updatedData[key as string] = {
      ids: updatedIds,
      count: Math.max(0, updatedIds.length),
      lastUpdated: Date.now(),
    }

    // 保存更新
    this.set(category, updatedData as any)
    this.set('lastUpdateTime', Date.now())

    // 如果是脚本相关操作，更新meta数据
    if (category === 'script.liker' || category === 'script.status') {
      this.updateScriptMeta(id, operation, remark || '')
    }
    else if (category === 'feedback.tags' || category === 'feedback.status') {
      this.updateFeedbackMeta(id, operation, remark || '')
    }
    else if (category === 'user.types') {
      this.updateUserMeta(id, operation, remark || '')
    }
  }

  /**
   * 处理互斥关系
   * @param key 当前操作的键
   * @param id 要处理的ID
   * @param updatedStats 待更新的统计对象
   */
  private handleMutualExclusion<C extends GreasyForkStorageCategory>(
    key: GreasyForkStorageKey<C>,
    id: string,
    updatedStats: Record<string, GreasyForkStatsItem>,
  ): void {
    // 互斥规则：除当前项外的所有其他项
    const allKeys = Object.keys(updatedStats).filter(k => k !== key)
    allKeys.forEach((otherKey) => {
      // 检查是否包含该ID
      const hasId = updatedStats[otherKey].ids.some(item => String(item.id) === String(id))
      if (hasId) {
        const updatedIds = updatedStats[otherKey].ids.filter(item => String(item.id) !== String(id))
        updatedStats[otherKey] = {
          ids: updatedIds,
          count: Math.max(0, updatedIds.length),
          lastUpdated: Date.now(),
        }
      }
    })
  }

  /**
   * 更新脚本元数据
   * @param scriptId 脚本ID
   * @param operation 操作类型
   * @param remark 备注
   */
  private updateScriptMeta(scriptId: string, operation: UpdateOperation, remark: string): void {
    const currentMeta = this.get('script.meta') as Array<{ id: string, name: string }>

    // 添加时才更新meta
    if (operation === UpdateOperation.Add) {
      // 如果不存在则添加
      if (!currentMeta.some(item => String(item.id) === String(scriptId))) {
        this.set('script.meta', [
          ...currentMeta,
          {
            id: scriptId,
            // 避免内容过长
            name: ToolKit.string.truncate(`${remark || `脚本 ${scriptId}`}`, 200),
          },
        ])
      }
    }
    else {
      // 检查是否还有其他引用
      const likerData = this.get('script.liker') as Record<string, GreasyForkStatsItem>
      const statusData = this.get('script.status') as Record<string, GreasyForkStatsItem>

      const hasOtherRefs
        = Object.values(likerData).some(item => item.ids.some(item => String(item.id) === String(scriptId)))
          || Object.values(statusData).some(item => item.ids.some(item => String(item.id) === String(scriptId)))

      // 如果没有其他引用，则移除meta
      if (!hasOtherRefs) {
        this.set('script.meta', currentMeta.filter(item => String(item.id) !== String(scriptId)))
      }
    }
  }

  /**
   * 更新反馈元数据
   * @param fullId 完整ID (格式: scriptId#discussionId)
   * @param operation 操作类型
   * @param remark 备注
   */
  private updateFeedbackMeta(fullId: string, operation: UpdateOperation, remark: string): void {
    const [scriptId, discussionId] = fullId.split('#')
    const [scriptName, description] = remark.split(':::')
    const currentMeta = this.get('feedback.meta') as Array<{ id: string, description: string }>

    // 添加时才更新meta
    if (operation === UpdateOperation.Add) {
      // 如果不存在则添加
      if (!currentMeta.some(item => String(item.id) === String(fullId))) {
        // 更新脚本元数据
        this.updateScriptMeta(scriptId, UpdateOperation.Add, scriptName)

        // 更新反馈元数据
        this.set('feedback.meta', [
          ...currentMeta,
          {
            id: fullId,
            // 避免内容过长
            description: ToolKit.string.truncate(description || `脚本 ${scriptId} 的讨论 ${discussionId}`, 200),
          },
        ])
      }
    }
    else {
      // 检查是否还有其他引用
      const tagsData = this.get('feedback.tags') as Record<string, GreasyForkStatsItem>
      const statusData = this.get('feedback.status') as Record<string, GreasyForkStatsItem>

      const hasOtherRefs
        = Object.values(tagsData).some(item => item.ids.some(item => String(item.id) === String(fullId)))
          || Object.values(statusData).some(item => item.ids.some(item => String(item.id) === String(fullId)))

      // 如果没有其他引用，则移除meta
      if (!hasOtherRefs) {
        this.set('feedback.meta', currentMeta.filter(item => String(item.id) !== String(fullId)))
      }
    }
  }

  /**
   * 更新用户元数据
   * @param userId 用户ID
   * @param operation 操作类型
   * @param remark 备注
   */
  private updateUserMeta(userId: string, operation: UpdateOperation, remark: string): void {
    const currentMeta = this.get('user.meta') as Array<{ id: string, name: string }>

    // 添加时才更新meta
    if (operation === UpdateOperation.Add) {
      // 如果不存在则添加
      if (!currentMeta.some(item => String(item.id) === String(userId))) {
        this.set('user.meta', [
          ...currentMeta,
          {
            id: userId,
            name: `${remark || `用户 ${userId}`}`,
          },
        ])
      }
    }
    else {
      // 检查是否还有其他引用
      const typesData = this.get('user.types') as Record<string, GreasyForkStatsItem>

      const hasOtherRefs
        = Object.values(typesData).some(item => item.ids.some(item => String(item.id) === String(userId)))

      // 如果没有其他引用，则移除meta
      if (!hasOtherRefs) {
        this.set('user.meta', currentMeta.filter(item => String(item.id) !== String(userId)))
      }
    }
  }

  /**
   * 根据脚本ID获取脚本名称
   * @param scriptId 脚本ID
   * @returns 脚本名称，如果不存在则返回默认名称
   */
  public getScriptName(scriptId: string): string {
    const meta = this.get('script.meta') as Array<{ id: string, name: string }>
    const found = meta.find(item => String(item.id) === String(scriptId))
    return found?.name || `脚本 ${scriptId}`
  }

  /**
   * 根据完整ID获取反馈描述
   * @param fullId 完整ID (格式: scriptId#discussionId)
   * @returns 反馈描述，如果不存在则返回默认描述
   */
  public getFeedbackDescription(fullId: string): string {
    const meta = this.get('feedback.meta') as Array<{ id: string, description: string }>
    const found = meta.find(item => String(item.id) === String(fullId))
    return found?.description || `讨论 ${fullId.split('#')[1]}`
  }

  /**
   * 根据用户 ID 获取用户名称
   * @param userId 用户 ID
   * @returns 用户名称，如果不存在则返回默认名称
   */
  public getUserName(userId: string): string {
    const meta = this.get('user.meta') as Array<{ id: string, name: string }>
    const found = meta.find(item => String(item.id) === String(userId))
    return found?.name || `用户 ${userId}`
  }

  /**
   * 根据用户 ID 获取用户类型
   * @param userId 用户 ID
   * @returns 用户类型数组
   */
  public getUserType(userId: string): GreasyForkUserType[] {
    return Object.entries(this.get('user.types')).filter(([_, data]) => {
      return data.ids.some(item => String(item.id) === String(userId))
    }).map(([type, _]) => type).filter(Boolean) as GreasyForkUserType[]
  }

  /**
   * 获取 ID 在指定统计项中的状态
   * @param id 要查询的ID
   * @param category 存储分类
   * @returns 包含ID的所有键名及其状态
   */
  public getStatus<C extends GreasyForkStorageCategory>(
    id: string,
    category: C,
  ): C extends 'script.liker'
    ? Record<ScriptFeedbackActionType, boolean>
    : C extends 'script.status'
      ? Record<ScriptStatusActionType, boolean>
      : C extends 'feedback.tags'
        ? Record<FeedbackTagType, boolean>
        : C extends 'feedback.status'
          ? Record<ResolutionStatusType, boolean>
          : C extends 'user.types'
            ? Record<GreasyForkUserType, boolean>
            : never {
    // 获取对应分类的数据
    const data = this.get(category as Path<GreasyForkStats>) as Record<string, GreasyForkStatsItem>

    const result = {} as any

    Object.keys(data).forEach((key) => {
      const item = data[key as keyof typeof data] as GreasyForkStatsItem
      result[key] = item.ids.some(item => String(item.id) === String(id))
    })

    return result
  }

  /**
   * 清除所有存储的偏好设置
   */
  public clear(): void {
    try {
      localStorage.removeItem(this.STORAGE_KEY)
      this.storage.delete(this.STORAGE_KEY)
    }
    catch (error) {
      console.error('Failed to clear preferences:', error)
    }
  }

  /**
   * 重置为默认设置
   */
  public resetToDefault(): void {
    Object.entries(DEFAULT_GREASY_FORK_STATS).forEach(([key, val]) =>
      this.set(key as keyof GreasyForkStats, val),
    )
  }
}
